package dev.ideas.funkyj.support;

import dev.ideas.funkyj.function.F;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
*/
@SuppressWarnings({"unchecked"})
class $ {

    static <X, Y> F<X, Y> create(X x, String ognl) {
        Class clazz = x.getClass();
        F<X, Y> f = null;

        for (String name : ognl.split("\\.")) {
            F<X, Y> f1;
            final AccessibleObject accessor = accessor(clazz, name);
            accessor.setAccessible(true); //todo try to avoid that

            if (accessor instanceof Method) {
                final Method method = (Method) accessor;
                f1 = new F<X, Y>() {
                    public Y apply(X x) {
                        try { return (Y) method.invoke(x); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); }
                    }
                };
                clazz = method.getReturnType();

            } else if (accessor instanceof Field) {
                final Field field = (Field) accessor;
                f1 = new F<X, Y>() {
                    public Y apply(X x) {
                        try { return (Y) field.get(x); } catch (IllegalAccessException e) { throw new RuntimeException(e); }
                    }
                };
                clazz = field.getType();
            } else { throw new UnsupportedOperationException(); }

            f = f == null ? f1 : f.dot((F<Y,Y>) f1);
        }
        return f;
    }

    private static AccessibleObject accessor(Class clazz, String name) {
        try { return clazz.getDeclaredMethod(getMethod(name)); } catch (NoSuchMethodException e1) {
            try { return clazz.getDeclaredMethod(isMethod(name)); } catch (NoSuchMethodException e2) {
                try { return clazz.getDeclaredField(name); } catch (NoSuchFieldException e3) {
                    throw new UnsupportedOperationException("no public getter method or field was found");
                }
            }
        }
    }

    private static String isMethod(String name) {return "is" + name.substring(0, 1).toUpperCase() + name.substring(1);}
    private static String getMethod(String name) {return "get" + name.substring(0, 1).toUpperCase() + name.substring(1);}
}
